import { readonly, ref } from 'vue';

/**
 * 打字机效果工具类
 * 提供多种打字机效果的实现方法
 */

export interface TypewriterOptions {
  /** 打字速度，毫秒 */
  speed?: number;
  /** 是否显示光标 */
  showCursor?: boolean;
  /** 光标字符 */
  cursorChar?: string;
  /** 是否循环打字 */
  loop?: boolean;
  /** 循环间隔时间，毫秒 */
  loopDelay?: number;
  /** 是否删除文本 */
  deleteText?: boolean;
  /** 删除速度，毫秒 */
  deleteSpeed?: number;
  /** 删除后暂停时间，毫秒 */
  pauseAfterDelete?: number;
  /** 开始打字前的延迟，毫秒 */
  startDelay?: number;
  /** 完成后的回调 */
  onComplete?: () => void;
  /** 每个字符打字后的回调 */
  onChar?: (char: string, index: number) => void;
}

export class Typewriter {
  private text: string = '';
  private currentIndex: number = 0;
  private isTyping: boolean = false;
  private isDeleting: boolean = false;
  private timeoutId: number | null = null;
  private options: Required<TypewriterOptions>;
  private displayText: string = '';
  private cursorVisible: boolean = true;

  constructor(options: TypewriterOptions = {}) {
    this.options = {
      speed: 50,
      showCursor: true,
      cursorChar: '|',
      loop: false,
      loopDelay: 2000,
      deleteText: false,
      deleteSpeed: 30,
      pauseAfterDelete: 1000,
      startDelay: 0,
      onComplete: () => {},
      onChar: () => {},
      ...options,
    };
  }

  /**
   * 开始打字机效果
   * @param text 要打字的文本
   * @param target 目标元素或回调函数
   */
  start(text: string, target?: HTMLElement | ((text: string) => void)): Promise<void> {
    return new Promise(resolve => {
      this.text = text;
      this.currentIndex = 0;
      this.isTyping = true;
      this.isDeleting = false;
      this.displayText = '';

      // 设置完成回调
      const originalOnComplete = this.options.onComplete;
      this.options.onComplete = () => {
        originalOnComplete();
        resolve();
      };

      // 开始延迟
      if (this.options.startDelay > 0) {
        setTimeout(() => {
          this.typeNextChar(target);
        }, this.options.startDelay);
      } else {
        this.typeNextChar(target);
      }
    });
  }

  /**
   * 停止打字机效果
   */
  stop(): void {
    this.isTyping = false;
    this.isDeleting = false;
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
      this.timeoutId = null;
    }
  }

  /**
   * 重置打字机状态
   */
  reset(): void {
    this.stop();
    this.text = '';
    this.currentIndex = 0;
    this.displayText = '';
    this.cursorVisible = true;
  }

  /**
   * 打字下一个字符
   */
  private typeNextChar(target?: HTMLElement | ((text: string) => void)): void {
    if (!this.isTyping) return;

    if (this.isDeleting) {
      // 删除模式
      this.displayText = this.text.substring(0, this.currentIndex - 1);
      this.currentIndex--;

      if (this.currentIndex === 0) {
        this.isDeleting = false;
        if (this.options.loop) {
          // 循环模式，暂停后重新开始
          this.timeoutId = window.setTimeout(() => {
            this.typeNextChar(target);
          }, this.options.loopDelay);
          return;
        } else {
          this.isTyping = false;
          this.options.onComplete();
          return;
        }
      }
    } else {
      // 打字模式
      this.displayText = this.text.substring(0, this.currentIndex + 1);
      this.currentIndex++;

      if (this.currentIndex === this.text.length) {
        // 打字完成
        if (this.options.deleteText && this.options.loop) {
          // 需要删除文本且循环模式
          this.timeoutId = window.setTimeout(() => {
            this.isDeleting = true;
            this.typeNextChar(target);
          }, this.options.pauseAfterDelete);
          return;
        } else {
          this.isTyping = false;
          this.options.onComplete();
          return;
        }
      }
    }

    // 更新显示
    this.updateDisplay(target);

    // 调用字符回调
    if (this.currentIndex > 0 && this.currentIndex <= this.text.length) {
      this.options.onChar(this.text[this.currentIndex - 1], this.currentIndex - 1);
    }

    // 继续下一个字符
    const delay = this.isDeleting ? this.options.deleteSpeed : this.options.speed;
    this.timeoutId = window.setTimeout(() => {
      this.typeNextChar(target);
    }, delay);
  }

  /**
   * 更新显示内容
   */
  private updateDisplay(target?: HTMLElement | ((text: string) => void)): void {
    let displayText = this.displayText;

    // 添加光标
    if (this.options.showCursor && this.cursorVisible) {
      displayText += this.options.cursorChar;
    }

    if (target) {
      if (target instanceof HTMLElement) {
        target.textContent = displayText;
      } else {
        target(displayText);
      }
    }
  }

  /**
   * 切换光标显示状态
   */
  toggleCursor(): void {
    this.cursorVisible = !this.cursorVisible;
  }

  /**
   * 获取当前显示文本
   */
  getDisplayText(): string {
    return this.displayText;
  }

  /**
   * 获取原始文本
   */
  getOriginalText(): string {
    return this.text;
  }

  /**
   * 是否正在打字
   */
  getIsTyping(): boolean {
    return this.isTyping;
  }
}

/**
 * 创建打字机实例的便捷函数
 * @param options 配置选项
 * @returns Typewriter实例
 */
export function createTypewriter(options?: TypewriterOptions): Typewriter {
  return new Typewriter(options);
}

/**
 * 简单的打字机效果函数
 * @param text 要打字的文本
 * @param target 目标元素或回调函数
 * @param options 配置选项
 * @returns Promise，打字完成后resolve
 */
export function typewriter(
  text: string,
  target: HTMLElement | ((text: string) => void),
  options?: TypewriterOptions
): Promise<void> {
  const typewriter = createTypewriter(options);
  return typewriter.start(text, target);
}

/**
 * 打字机效果的Vue组合式函数
 * @param options 配置选项
 * @returns 打字机相关的响应式状态和方法
 */
export function useTypewriter(options?: TypewriterOptions) {
  const typewriter = createTypewriter(options);
  const isTyping = ref(false);
  const displayText = ref('');

  const start = (text: string) => {
    isTyping.value = true;
    return typewriter
      .start(text, (text: string) => {
        displayText.value = text;
      })
      .finally(() => {
        isTyping.value = false;
      });
  };

  const stop = () => {
    typewriter.stop();
    isTyping.value = false;
  };

  const reset = () => {
    typewriter.reset();
    displayText.value = '';
    isTyping.value = false;
  };

  return {
    isTyping: readonly(isTyping),
    displayText: readonly(displayText),
    start,
    stop,
    reset,
  };
}
